package com.leenmvc.core.exception;

import com.leenmvc.core.security.User;
import com.leenmvc.core.security.dao.core.SysPermissionDao;
import com.leenmvc.core.security.dao.core.SysTipDao;
import com.leenmvc.core.security.entity.core.SysLogError;
import com.leenmvc.core.security.entity.core.SysPermission;
import com.leenmvc.core.utils.StringUtils;
import com.leenmvc.core.utils.TipUtil;
import com.leenmvc.core.utils.http.HttpContextUtils;
import com.leenmvc.core.utils.thread.ThreadLocalManager;
import com.leenmvc.core.utils.thread.runnables.LogToDb;
import com.leenmvc.core.base.result.Result;
import com.leenmvc.core.base.result.ResultCode;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.bouncycastle.asn1.ocsp.ResponseData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.Date;


/**
 * 异常处理器
 *
 * @author Mark hongjl
 * @since 1.0.0
 */
@RestControllerAdvice
public class GlobalRestControllerExceptionHandler {

    private final static Logger logger = LoggerFactory.getLogger(GlobalRestControllerExceptionHandler.class);

    @Autowired
    private SysPermissionDao sysPermissionDao;

    @Autowired
    private SysTipDao sysTipDao;

    /**
     * 没有requestBody但是方法要求参数为RequestBody时报错
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result handleRenException(HttpMessageNotReadableException ex) {
        ex.printStackTrace();

        return unifiedReturn(ResultCode.INFO, TipUtil.getTip("EXCEPTION_BODY_REQUIRE"));
    }

    /**
     * 处理自定义业务层异常
     */
    @ExceptionHandler(BusinessException.class)
    public Result handleRenException(BusinessException ex) {
        ex.printStackTrace();
        return unifiedReturn(ResultCode.INFO, ex);
    }

    /**
     * 处理自定义业务层异常
     */
    @ExceptionHandler(AuthorizedException.class)
    public Result handleRenException(AuthorizedException ex) {
        ex.printStackTrace();
        return unifiedReturn(ResultCode.NO_AUTHORIZED, ex);
    }

    /**
     * 处理自定义数据持久层异常
     * 该异常必须处理，并且不会被在前端展示
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(DaoException.class)
    public Result handleException(DaoException ex) {
        ex.printStackTrace();
        if (ex.getShow()) {
            return unifiedReturn(ResultCode.INFO, ex);
        };
        return unifiedReturn(ResultCode.FAIL, ex);
    }

    /**
     * 处理自定义数据校验异常
     * 校验异常可能出现在持久层，选择性显示在前端
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(ValidatorException.class)
    public Result handleException(ValidatorException ex) {
        ex.printStackTrace();
        if (ex.getShow()) {
            return unifiedReturn(ResultCode.INFO, ex);
        }
        return unifiedReturn(ResultCode.FAIL, ex);
    }

    /**
     * 无权限
     *
     * 校验异常可能出现在持久层，选择性显示在前端
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(UnauthorizedException.class)
    public Result handleException(UnauthorizedException ex) {
        ex.printStackTrace();
        String message = ex.getMessage();
        message = message.substring(message.indexOf("[")+1,message.lastIndexOf("]"));
        String permission = StringUtils.getMatchStr("\\[(.*?)\\]", ex.getMessage(), 1);
        SysPermission identification = sysPermissionDao.get("identification", message);
        logger.info("捕获到一个权限不足异常, 异常信息为：{}, 权限标识为:{}", message, permission);
        if(identification == null ){
            return unifiedReturn(ResultCode.INFO, TipUtil.getTip("EXCEPTION_POWER_NOT_OPEN", permission));
        }
        return unifiedReturn(ResultCode.INFO, TipUtil.getTip("EXCEPTION_UNAUTHORIZED", identification.getModule()));
    }

    /**
     * 处理自定义数据校验异常
     * 校验异常可能出现在持久层，选择性显示在前端
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(NumberFormatException.class)
    public Result handleException(NumberFormatException ex) {
        ex.printStackTrace();
        return unifiedReturn(ResultCode.FAIL, ex);
    }

    /**
     * 请求方式被拒绝
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result handleException(HttpRequestMethodNotSupportedException ex) {
        ex.printStackTrace();
        return unifiedReturn(ResultCode.INFO, TipUtil.getTip("EXCEPTION_WRONG_METHOD"));
    }

    /**
     * 顶级异常父类 (如果到了这里必须处理)
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception ex) {
        ex.printStackTrace();
        return unifiedReturn(ResultCode.FAIL, ex);
    }

    /**
     * 请求接口的时候缺少必要的参数
     * Required Long parameter 'userId' is not present
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result handleException(MissingServletRequestParameterException ex) {
        String paramName = StringUtils.getMatchStr("\'.*?\'", ex.getMessage());
        logger.info("捕获一个全局异常，异常信息为: {}， 缺少的参数是: {}", ex.getMessage(), paramName);
        saveLog(ex);
        return unifiedReturn(ResultCode.INFO, TipUtil.getTip("EXCEPTION_PARAM_LACK", paramName));
    }

    /**
     * 请求接口参数转换错误
     *
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result handleException(MethodArgumentTypeMismatchException ex) {
        String paramName = StringUtils.getMatchStr("\".*?\"", ex.getMessage());
        logger.info("捕获一个全局异常，异常信息为: {}, 参数为： {}", ex.getMessage(), paramName);
        return unifiedReturn(ResultCode.INFO, TipUtil.getTip("EXCEPTION_PARAM_WRONT_CONVERT", paramName));
    }

    /**
     * 权限不足
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(AuthorizationException.class)
    public Result handlerException(AuthorizationException ex) {
        logger.info("捕获一个全局异常，异常信息为: {}, 当前用户没有操作权限！！", ex.getMessage());
        String permission = StringUtils.getMatchStr("\\[(.*?)\\]", ex.getMessage(), 1);
        SysPermission perm = sysPermissionDao.get("identification", permission);
        if (perm == null) {
            return unifiedReturn(ResultCode.NO_AUTHORIZED, TipUtil.getTip("EXCEPTION_POWER_NOT_OPEN", permission));
        }
        return unifiedReturn(ResultCode.NO_AUTHORIZED, TipUtil.getTip("EXCEPTION_NO_POWER", perm.getModule(), perm.getName(), perm.getIdentification()));
    }

    /**
     * 主键重复
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public Result handlerException(SQLIntegrityConstraintViolationException ex) {
        ex.printStackTrace();
        return unifiedReturn(ResultCode.FAIL, ex);
    }

    /**
     * 统一的返回结果处理，方便做国际化
     * 1.百度翻译
     * 2.数据库配置
     *
     * @return
     */
    private Result unifiedReturn(ResultCode resultCode, String message) {
        switch (resultCode) {
            case SUCCESS:
                return new Result().ok(message);
            case FAIL:
                return new Result().error(message);
            case INFO:
                return new Result().info(message);
            case NO_AUTHORIZED:
                return new Result().unauthorized(message);
            default:
        }
        return null;
    }

    /**
     * 统一的返回结果处理，方便做国际化
     * 1.百度翻译
     * 2.数据库配置
     *
     * @return
     */
    private Result unifiedReturn(ResultCode resultCode, Exception ex) {
        switch (resultCode) {
            case SUCCESS:
                return new Result();
            case FAIL:
                String message = ex.getMessage();
                saveLog(ex);
                return new Result().error(message);
            case INFO:
                return new Result().info(ex.getMessage());
            case NO_AUTHORIZED:
                return new Result().unauthorized(ex.getMessage());
            default:
        }
        return null;
    }

    /**
     * 将异常信息加入到异常日志表中
     *
     * @param ex
     */
    private void saveLog(Exception ex) {
        HttpServletRequest request = ThreadLocalManager.getRequest();
        SysLogError sysLogError = new SysLogError();
        sysLogError.setErrorInfo(getStackTrace(ex));
        sysLogError.setIp(HttpContextUtils.getIpAddr(request));
        sysLogError.setRequestMethod(request.getMethod());
        sysLogError.setCreator(User.getId());
        sysLogError.setCreateDate(new Date());
        sysLogError.setRequestUri(request.getRequestURI());
        sysLogError.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
        sysLogError.setRequestParams(String.valueOf(ThreadLocalManager.get("requestParams")));
        LogToDb.addErrorlog(sysLogError);
    }

    /**
     * 获取错误的堆栈信息
     *
     * @param throwable
     * @return
     */
    public String getStackTrace(Throwable throwable) {
        StringWriter stringWriter = new StringWriter();
        PrintWriter printWriter = new PrintWriter(stringWriter);
        try {
            throwable.printStackTrace(printWriter);
            return stringWriter.toString();
        }catch (Exception e){
            e.printStackTrace();
        } finally {
            printWriter.close();
        }
        return null;
    }

}
